package com.app.baselibrary.utils

import android.content.Context
import android.os.Build
import android.os.Environment
import android.util.Log
import com.app.baselibrary.ktx.APPLICATION
import com.app.baselibrary.ktx.toJson
import java.io.File
import java.io.FileOutputStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicReference


/**
 * 日志工具类
 */
object LogUtil {

    private const val ONE_DAY = 24 * 60 * 60 * 1000L

    private const val KEY_FILE_LOG = "key_log_file"

    private val sampleDateFormat by lazy {
        AtomicReference(SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS", Locale.CHINA))
    }

    private val logThread by lazy {
        Executors.newSingleThreadExecutor()
    }

    /**
     * 默认保存两天的日志
     */
    private var sSaveDay = 2
    private var logFileAndConsole: Boolean = false

    private val dir by lazy {
        File(APPLICATION.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "log")
    }

    private val logSp by lazy { APPLICATION.getSharedPreferences("log-info", Context.MODE_PRIVATE) }

    private fun checkLogFile(){
        if (!dir.exists()){
            dir.mkdir()
        }
    }

    private fun createLogFile(fileName: String): File{
        val file = File(dir, fileName)
        val header = StringBuilder()
        header.append("*********header********\n")
        header.append("系统版本：" + Build.VERSION.RELEASE + "\n")
        header.append("设备型号：" + Build.MODEL + "\n")
        header.append("*********header********\n\n")
        getLogFileOutputStream(file).write(header.toString().toByteArray())
        return file
    }

    /**
     * 获取写入文件的流对象
     */
    private fun getLogFileOutputStream(file: File): FileOutputStream {
        checkLogFile()
        return FileOutputStream(file, true)
    }

    /**
     * 保存的天数
     */
    @JvmStatic
    fun setSaveDay(days: Int){
        sSaveDay = days
    }

    /**
     * 输出文件日志的同时输出到控制台
     */
    @JvmStatic
    fun logFileAndConsole(open: Boolean = true){
        logFileAndConsole = open
    }

    @JvmStatic
    fun logI(msg: Any){
        val throwable = Throwable()
        val logBean = convertLog(throwable, msg)
        Log.i(logBean.tag, logBean.msg)
    }

    @JvmStatic
    fun logD(msg: Any){
        val throwable = Throwable()
        val logBean = convertLog(throwable, msg)
        Log.d(logBean.tag, logBean.msg)
    }

    @JvmStatic
    fun logW(msg: Any){
        val throwable = Throwable()
        val logBean = convertLog(throwable, msg)
        Log.w(logBean.tag, logBean.msg)
    }

    @JvmStatic
    fun logE(msg: Any){
        val throwable = Throwable()
        val logBean = convertLog(throwable, msg)
        Log.e(logBean.tag, logBean.msg)
    }

    @JvmStatic
    fun logFileI(msg: Any){
        val throwable = Throwable()
        val logBean = convertLog(throwable, msg)
        logFile(LogFileType.INFO,logBean.tag, logBean.msg)
    }

    @JvmStatic
    fun logFileD(msg: Any){
        val throwable = Throwable()
        val logBean = convertLog(throwable, msg)
        logFile(LogFileType.DEBUG,logBean.tag, logBean.msg)

    }

    @JvmStatic
    fun logFileW(msg: Any){
        val throwable = Throwable()
        val logBean = convertLog(throwable, msg)
        logFile(LogFileType.WRONG,logBean.tag, logBean.msg)
    }

    @JvmStatic
    fun logFileE(msg: Any){
        val throwable = Throwable()
        val logBean = convertLog(throwable, msg)
        logFile(LogFileType.ERROR,logBean.tag, logBean.msg)
    }

    @Synchronized
    private fun logFile(type: LogFileType, tag: String, msg: String){

        if (logFileAndConsole){
            when(type){
                LogFileType.INFO -> Log.i(tag, msg)
                LogFileType.DEBUG -> Log.d(tag, msg)
                LogFileType.WRONG -> Log.w(tag, msg)
                LogFileType.ERROR -> Log.e(tag, msg)
            }
        }
        logThread.submit {
            val logFile = getActiveLogFile()

            val logString = StringBuilder()
            logString.append("\n" + sampleDateFormat.get().format(Date()))
            logString.append("\t" + type.name + "/" + tag + ": ")
            logString.append(msg + "\n")

            getLogFileOutputStream(logFile).write(logString.toString().toByteArray())
        }
    }


    /**
     * 转换消息文本
     */
    private fun convertLog(throwable: Throwable, msg: Any?): LogBean {
        val message: String = when (msg) {
            null -> {
                "NULL"
            }
            is String -> {
                msg
            }
            else -> {
                msg.toJson
            }
        }
        val stringBuilder = java.lang.StringBuilder()
        val trace = throwable.stackTrace[1]
        val fileName = trace.fileName
        val line = trace.lineNumber
        val methodName = trace.methodName
        stringBuilder.append("FileName:($fileName :$line)\n")
        stringBuilder.append("method: $methodName\n")
        stringBuilder.append("thread: ${Thread.currentThread().name}\n")
        stringBuilder.append("-----------------------\n")
        stringBuilder.append(message + "\n")
        stringBuilder.append("-----------------------\n")
        return LogBean(fileName, stringBuilder.toString())
    }


    private fun getActiveLogFile(): File {
        checkLogFile()
        val dateStr = logSp.getString(KEY_FILE_LOG, "")?: ""
        val logFile: File
        if (dateStr.isNotEmpty()){
            //判断是否过期
            val file = File(dir, dateStr)
            if (file.exists()){
                val fileName = file.name
                val lastTime = fileName.split("-")[1].toLong()
                logFile = if (Date().time - lastTime > ONE_DAY * sSaveDay){
                    file.delete()
                    //删掉日志文件
                    val newFileName = "log-${Date().time}"
                    logSp.edit().putString(KEY_FILE_LOG, newFileName).apply()
                    createLogFile(newFileName)
                }else{
                    file
                }
            }else{
                //没有日志文件，可能用户删掉了
                val fileName = "log-${Date().time}"
                logSp.edit().putString(KEY_FILE_LOG, fileName).apply()
                logFile = createLogFile(fileName)
            }
        }else{
            //没有创建日志文件
            val fileName = "log-${Date().time}"
            logSp.edit().putString(KEY_FILE_LOG, fileName).apply()
            logFile = createLogFile(fileName)
        }
        return logFile
    }
    /**
     * 文件日志类型
     */
    enum class LogFileType {

        INFO,

        DEBUG,

        WRONG,

        ERROR;

    }


    data class LogBean(val tag: String, val msg: String)

}